package info.thereisonlywe.core.essentials;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JTable;

/**
 * 
 * @author thereisonlywe
 */
public class UIEssentials {

	public static class Colors {

		public static final Color ROYAL_MAROON = new Color(90, 56, 57);
		public static final Color SWEDISH_AZURE = new Color(0, 91, 153);
		public static final Color APPLE_GRAY = new Color(214, 217, 223);
		public static final Color FOREST_GREEN = new Color(34, 139, 34);
		public static final Color DARTHMOUTH_GREEN = new Color(0, 112, 60);

		public static String toHex(int i, int j, int k)
		{
			Color c = new Color(i, j, k);
			String tmp = Integer.toHexString(c.getRGB());
			return new String(tmp.substring(2, tmp.length()));
		}
	}

	// http://www.chka.de/swing/table/scrolling.html
	public static class Scrolling {
		public static final int VIEWPORT = 0, // take the policy of the
												// viewport
				UNCHANGED = 1, // don't scroll if it fills the visible area,
								// otherwise take the policy of the viewport
				FIRST = 2, // scroll the first part of the region into view
				CENTER = 3, // center the region
				LAST = 4; // scroll the last part of the region into view

		public static final int NONE = 0, TOP = 1, VCENTER = 2, BOTTOM = 4,
				LEFT = 8, HCENTER = 16, RIGHT = 32;

		private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);

		public static void scroll(JComponent c, int part)
		{
			scroll(c, part & (LEFT | HCENTER | RIGHT), part
					& (TOP | VCENTER | BOTTOM));
		}

		public static void scroll(JComponent c, int horizontal, int vertical)
		{
			Rectangle visible = c.getVisibleRect();
			Rectangle bounds = c.getBounds();

			switch (vertical)
			{
			case TOP:
				visible.y = 0;
				break;
			case VCENTER:
				visible.y = (bounds.height - visible.height) / 2;
				break;
			case BOTTOM:
				visible.y = bounds.height - visible.height;
				break;
			}

			switch (horizontal)
			{
			case LEFT:
				visible.x = 0;
				break;
			case HCENTER:
				visible.x = (bounds.width - visible.width) / 2;
				break;
			case RIGHT:
				visible.x = bounds.width - visible.width;
				break;
			}

			c.scrollRectToVisible(visible);
		}

		/*---------------------------------------------------------------
		  ScrollingToolkit with bias.
		 */
		public static void scroll(JComponent c, Rectangle r, int bias)
		{
			scroll(c, r, bias, bias);
		}

		public static void scroll(JComponent c, Rectangle r,
				int horizontalBias, int verticalBias)
		{
			Rectangle visible = c.getVisibleRect(), dest = new Rectangle(r);

			if (dest.width > visible.width)
			{
				if (horizontalBias == VIEWPORT)
				{
					// leave as is
				}
				else if (horizontalBias == UNCHANGED)
				{
					if (dest.x <= visible.x
							&& dest.x + dest.width >= visible.x + visible.width)
					{
						dest.x = visible.x;
						dest.width = visible.width;
					}
				}
				else
				{
					if (horizontalBias == CENTER) dest.x += (dest.width - visible.width) / 2;
					else if (horizontalBias == LAST)
						dest.x += dest.width - visible.width;

					dest.width = visible.width;
				}
			}

			if (dest.height > visible.height)
			{
				if (verticalBias == VIEWPORT)
				{
					// leave as is
				}
				else if (verticalBias == UNCHANGED)
				{
					if (dest.y <= visible.y
							&& dest.y + dest.height >= visible.y
									+ visible.height)
					{
						dest.y = visible.y;
						dest.height = visible.height;
					}
				}
				else
				{
					if (verticalBias == CENTER) dest.y += (dest.height - visible.height) / 2;
					else if (verticalBias == LAST)
						dest.y += dest.height - visible.height;

					dest.height = visible.height;
				}
			}

			if (!visible.contains(dest)) c.scrollRectToVisible(dest);
		}

		/*--------------------------------------------------------
		  One-direction scrolling.
		 */

		public static void scrollHorizontally(JComponent c, Rectangle r)
		{
			scrollHorizontally(c, r.x, r.x + r.width);
		}

		public static void scrollHorizontally(JComponent c, int from, int to)
		{
			Rectangle visible = c.getVisibleRect();

			if (visible.x <= from && visible.x + visible.width >= to) return;

			visible.x = from;
			visible.width = to - from;

			c.scrollRectToVisible(visible);
		}

		public static void scrollHorizontally(JComponent c, Rectangle r,
				int bias)
		{
			scrollHorizontally(c, r.x, r.x + r.width, bias);
		}

		public static void scrollHorizontally(JComponent c, int from, int to,
				int bias)
		{
			Rectangle visible = c.getVisibleRect(), dest = new Rectangle(
					visible);

			dest.x = from;
			dest.width = to - from;

			if (dest.width > visible.width)
			{
				if (bias == VIEWPORT)
				{
					// leave as is
				}
				else if (bias == UNCHANGED)
				{
					if (dest.x <= visible.x
							&& dest.x + dest.width >= visible.x + visible.width)
					{
						dest.x = visible.x;
						dest.width = visible.width;
					}
				}
				else
				{
					if (bias == CENTER) dest.x += (dest.width - visible.width) / 2;
					else if (bias == LAST)
						dest.x += dest.width - visible.width;

					dest.width = visible.width;
				}
			}

			if (!visible.contains(dest)) c.scrollRectToVisible(dest);
		}

		public static void scrollVertically(JComponent c, Rectangle r)
		{
			scrollVertically(c, r.y, r.y + r.height);
		}

		public static void scrollVertically(JComponent c, int from, int to)
		{
			Rectangle visible = c.getVisibleRect();

			if (visible.y <= from && visible.y + visible.height >= to) return;

			visible.y = from;
			visible.height = to - from;

			c.scrollRectToVisible(visible);
		}

		public static void scrollVertically(JComponent c, Rectangle r, int bias)
		{
			scrollVertically(c, r.y, r.y + r.height, bias);
		}

		public static void scrollVertically(JComponent c, int from, int to,
				int bias)
		{
			Rectangle visible = c.getVisibleRect(), dest = new Rectangle(
					visible);

			dest.y = from;
			dest.height = to - from;

			if (dest.height > visible.height)
			{
				if (bias == VIEWPORT)
				{
					// leave as is
				}
				else if (bias == UNCHANGED)
				{
					if (dest.y <= visible.y
							&& dest.y + dest.height >= visible.y
									+ visible.height)
					{
						dest.y = visible.y;
						dest.height = visible.height;
					}
				}
				else
				{
					if (bias == CENTER) dest.y += (dest.height - visible.height) / 2;
					else if (bias == LAST)
						dest.y += dest.height - visible.height;

					dest.height = visible.height;
				}
			}

			if (!visible.contains(dest)) c.scrollRectToVisible(dest);
		}

		/*----------------------------------------------------------
		  Centering.
		 */

		public static void center(JComponent c, Rectangle r, boolean withInsets)
		{
			Rectangle visible = c.getVisibleRect();

			visible.x = r.x - (visible.width - r.width) / 2;
			visible.y = r.y - (visible.height - r.height) / 2;

			Rectangle bounds = c.getBounds();
			Insets i = withInsets ? EMPTY_INSETS : c.getInsets();
			bounds.x = i.left;
			bounds.y = i.top;
			bounds.width -= i.left + i.right;
			bounds.height -= i.top + i.bottom;

			if (visible.x < bounds.x) visible.x = bounds.x;

			if (visible.x + visible.width > bounds.x + bounds.width)
				visible.x = bounds.x + bounds.width - visible.width;

			if (visible.y < bounds.y) visible.y = bounds.y;

			if (visible.y + visible.height > bounds.y + bounds.height)
				visible.y = bounds.y + bounds.height - visible.height;

			c.scrollRectToVisible(visible);
		}

		public static void centerHorizontally(JComponent c, Rectangle r,
				boolean withInsets)
		{
			centerHorizontally(c, r.x, r.x + r.width, withInsets);
		}

		public static void centerHorizontally(JComponent c, int from, int to,
				boolean withInsets)
		{
			Rectangle bounds = c.getBounds();
			Insets i = withInsets ? EMPTY_INSETS : c.getInsets();
			bounds.x = i.left;
			bounds.y = i.top;
			bounds.width -= i.left + i.right;
			bounds.height -= i.top + i.bottom;

			Rectangle visible = c.getVisibleRect();

			visible.x = from - (visible.width + from - to) / 2;

			if (visible.x < bounds.x) visible.x = bounds.x;

			if (visible.x + visible.width > bounds.x + bounds.width)
				visible.x = bounds.x + bounds.width - visible.width;

			c.scrollRectToVisible(visible);
		}

		public static void centerVertically(JComponent c, Rectangle r,
				boolean withInsets)
		{
			centerVertically(c, r.y, r.y + r.height, withInsets);
		}

		public static void centerVertically(JComponent c, int from, int to,
				boolean withInsets)
		{
			Rectangle bounds = c.getBounds();
			Insets i = withInsets ? EMPTY_INSETS : c.getInsets();
			bounds.x = i.left;
			bounds.y = i.top;
			bounds.width -= i.left + i.right;
			bounds.height -= i.top + i.bottom;

			Rectangle visible = c.getVisibleRect();

			visible.y = from - (visible.height + from - to) / 2;

			if (visible.y < bounds.y) visible.y = bounds.y;

			if (visible.y + visible.height > bounds.y + bounds.height)
				visible.y = bounds.y + bounds.height - visible.height;

			c.scrollRectToVisible(visible);
		}

		/*-----------------------------------------------------------
		  Visibility.
		 */

		public static boolean isVisible(JComponent c, Rectangle r)
		{
			return c.getVisibleRect().contains(r);
		}

		public static boolean isHorizontallyVisible(JComponent c, int from,
				int to)
		{
			Rectangle visible = c.getVisibleRect();

			return visible.x <= from && visible.x + visible.width >= to;
		}

		public static boolean isHorizontallyVisible(JComponent c, Rectangle r)
		{
			Rectangle visible = c.getVisibleRect();

			return visible.x <= r.x
					&& visible.x + visible.width >= r.x + r.width;
		}

		public static boolean isVerticallyVisible(JComponent c, int from, int to)
		{
			Rectangle visible = c.getVisibleRect();

			return visible.y <= from && visible.y + visible.height >= to;
		}

		public static boolean isVerticallyVisible(JComponent c, Rectangle r)
		{
			Rectangle visible = c.getVisibleRect();

			return visible.y <= r.y
					&& visible.y + visible.height >= r.y + r.height;
		}

		// Scrolling toolkit for JTable
		public static class Table {
			public static Rectangle getRowBounds(JTable table, int row)
			{
				checkRow(table, row);

				Rectangle result = table.getCellRect(row, -1, true);
				Insets i = table.getInsets();

				result.x = i.left;
				result.width = table.getWidth() - i.left - i.right;

				return result;
			}

			public static Rectangle getRowBounds(JTable table, int first,
					int last)
			{
				checkRows(table, first, last);

				Rectangle result = table.getCellRect(first, -1, true);
				result = result.union(table.getCellRect(last, -1, true));
				Insets i = table.getInsets();

				result.x = i.left;
				result.width = table.getWidth() - i.left - i.right;

				return result;
			}

			public static Rectangle getColumnBounds(JTable table, int column)
			{
				checkColumn(table, column);

				Rectangle result = table.getCellRect(-1, column, true);
				Insets i = table.getInsets();

				result.y = i.top;
				result.height = table.getHeight() - i.top - i.bottom;

				return result;
			}

			public static Rectangle getColumnBounds(JTable table, int first,
					int last)
			{
				checkColumns(table, first, last);

				Rectangle result = table.getCellRect(-1, first, true);
				result = result.union(table.getCellRect(-1, last, true));
				Insets i = table.getInsets();

				result.y = i.top;
				result.height = table.getHeight() - i.top - i.bottom;

				return result;
			}

			/** For completeness. Only allows valid rows/columns. */
			public static Rectangle getCellBounds(JTable table, int row,
					int column)
			{
				checkCell(table, row, column);

				return table.getCellRect(row, column, true);
			}

			public static Rectangle getCellBounds(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn)
			{
				checkCells(table, firstRow, lastRow, firstColumn, lastColumn);

				Rectangle result = table.getCellRect(firstRow, firstColumn,
						true);
				return result.union(table
						.getCellRect(lastRow, lastColumn, true));
			}

			public static void makeRowVisible(JTable table, int row)
			{
				Scrolling.scrollVertically(table, getRowBounds(table, row));
			}

			public static void makeColumnVisible(JTable table, int column)
			{
				Scrolling.scrollHorizontally(table,
						getColumnBounds(table, column));
			}

			public static void makeRowsVisible(JTable table, int first, int last)
			{
				Scrolling.scrollVertically(table,
						getRowBounds(table, first, last));
			}

			public static void makeRowsVisible(JTable table, int first,
					int last, int bias)
			{
				Scrolling.scrollVertically(table,
						getRowBounds(table, first, last), bias);
			}

			public static void makeColumnsVisible(JTable table, int first,
					int last)
			{
				Scrolling.scrollHorizontally(table,
						getColumnBounds(table, first, last));
			}

			public static void makeColumnsVisible(JTable table, int first,
					int last, int bias)
			{
				Scrolling.scrollHorizontally(table,
						getColumnBounds(table, first, last), bias);
			}

			public static void makeCellsVisible(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn)
			{
				table.scrollRectToVisible(getCellBounds(table, firstRow,
						lastRow, firstColumn, lastColumn));
			}

			public static void makeCellsVisible(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn, int bias)
			{
				Scrolling.scroll(
						table,
						getCellBounds(table, firstRow, lastRow, firstColumn,
								lastColumn), bias);
			}

			public static void makeCellsVisible(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn, int rowBias,
					int columnBias)
			{
				Scrolling.scroll(
						table,
						getCellBounds(table, firstRow, lastRow, firstColumn,
								lastColumn), rowBias, columnBias);
			}

			public static void centerRow(JTable table, int row)
			{
				Scrolling.centerVertically(table, getRowBounds(table, row),
						false);
			}

			public static void centerColumn(JTable table, int column)
			{
				Scrolling.centerHorizontally(table,
						getColumnBounds(table, column), false);
			}

			public static void centerRows(JTable table, int first, int last)
			{
				Scrolling.centerVertically(table,
						getRowBounds(table, first, last), false);
			}

			public static void centerColumns(JTable table, int first, int last)
			{
				Scrolling.centerHorizontally(table,
						getColumnBounds(table, first, last), false);
			}

			public static void centerCell(JTable table, int row, int column)
			{
				Scrolling.center(table, getCellBounds(table, row, column),
						false);
			}

			public static void centerCells(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn)
			{
				Scrolling.center(
						table,
						getCellBounds(table, firstRow, lastRow, firstColumn,
								lastColumn), false);
			}

			public static boolean isRowVisible(JTable table, int row)
			{
				return Scrolling.isVerticallyVisible(table,
						getRowBounds(table, row));
			}

			public static boolean isColumnVisible(JTable table, int column)
			{
				return Scrolling.isHorizontallyVisible(table,
						getColumnBounds(table, column));
			}

			public static boolean isCellVisible(JTable table, int row,
					int column)
			{
				return Scrolling.isVisible(table,
						getCellBounds(table, row, column));
			}

			public static boolean areColumnsVisible(JTable table, int first,
					int last)
			{
				return Scrolling.isHorizontallyVisible(table,
						getColumnBounds(table, first, last));
			}

			public static boolean areRowsVisible(JTable table, int first,
					int last)
			{
				return Scrolling.isVerticallyVisible(table,
						getRowBounds(table, first, last));
			}

			public static boolean areCellsVisible(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn)
			{
				checkCells(table, firstRow, lastRow, firstColumn, lastColumn);

				return Scrolling.isVisible(
						table,
						getCellBounds(table, firstRow, lastRow, firstColumn,
								lastColumn));
			}

			private static void checkRow(JTable table, int row)
			{
				if (row < 0) throw new IndexOutOfBoundsException(row + " < 0");
				if (row >= table.getRowCount())
					throw new IndexOutOfBoundsException(row + " >= "
							+ table.getRowCount());
			}

			private static void checkColumn(JTable table, int column)
			{
				if (column < 0)
					throw new IndexOutOfBoundsException(column + " < 0");
				if (column >= table.getColumnCount())
					throw new IndexOutOfBoundsException(column + " >= "
							+ table.getColumnCount());
			}

			private static void checkCell(JTable table, int row, int column)
			{
				checkRow(table, row);
				checkColumn(table, column);
			}

			private static void checkRows(JTable table, int first, int last)
			{
				if (first < 0)
					throw new IndexOutOfBoundsException(first + " < 0");
				if (first > last)
					throw new IndexOutOfBoundsException(first + " > " + last);
				if (last >= table.getRowCount())
					throw new IndexOutOfBoundsException(last + " >= "
							+ table.getRowCount());
			}

			private static void checkColumns(JTable table, int first, int last)
			{
				if (first < 0)
					throw new IndexOutOfBoundsException(first + " < 0");
				if (first > last)
					throw new IndexOutOfBoundsException(first + " > " + last);
				if (last >= table.getColumnCount())
					throw new IndexOutOfBoundsException(last + " >= "
							+ table.getColumnCount());
			}

			private static void checkCells(JTable table, int firstRow,
					int lastRow, int firstColumn, int lastColumn)
			{
				checkRows(table, firstRow, lastRow);
				checkColumns(table, firstColumn, lastColumn);
			}
		}
	}

	public static void showErrorDialog(String title, String body)
	{
		JOptionPane.showOptionDialog(null, body, title,
				JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null,
				null, null);
	}

	public static void showInformationDialog(String title, String body)
	{
		JOptionPane.showOptionDialog(null, body, title,
				JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE,
				null, null, null);
	}

	public static void showWarningDialog(String title, String body)
	{
		JOptionPane.showOptionDialog(null, body, title,
				JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE, null,
				null, null);
	}

	public static int showConfirmDialog(String title, String body, int maxChars)
	{
		int val = JOptionPane.showConfirmDialog(null,
				StringEssentials.multiLine(body, maxChars), title,
				JOptionPane.YES_NO_OPTION);
		return val;
	}

	public static Point getMouseLocation()
	{
		PointerInfo pi = MouseInfo.getPointerInfo();
		return pi.getLocation();
	}

	public static Point getCenteredWindowPosition(int frameW, int frameH)
	{
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		int width = (int) screenSize.getWidth();
		int height = (int) screenSize.getHeight();
		width = (width - frameW) / 2;
		height = (height - frameH) / 2;
		return new Point(width, height);
	}

	public static void setClipboardContents(String aString)
	{
		StringSelection stringSelection = new StringSelection(aString);
		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		clipboard.setContents(stringSelection, null);
	}

	public static String getClipboardContents()
	{
		String result = "";
		Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
		Transferable contents = clipboard.getContents(null);
		boolean hasTransferableText = (contents != null)
				&& contents.isDataFlavorSupported(DataFlavor.stringFlavor);
		if (hasTransferableText)
		{
			try
			{
				result = (String) contents
						.getTransferData(DataFlavor.stringFlavor);
			}
			catch (UnsupportedFlavorException ex)
			{
			}
			catch (IOException ex)
			{
			}
		}
		return result;
	}

	public static Font changeFontSize(Font f, int offset)
	{
		int size = f.getSize();
		size += offset;
		int style = f.getStyle();
		return f.deriveFont(style, size);
	}

}